import { Sudoku } from './sudoku'
export interface Command {
  // Execute one command, return false if state not change
  execute(sudoku: Sudoku): boolean
  // Recover one command (use for undo)
  recover(sudoku: Sudoku): void
}

export interface CmdParam {
  rowIdx: number
  colIdx: number
  value: number
}

export class CertainNumCmd {
  protected param: CmdParam
  protected oldNum: number = -1

  constructor(param: CmdParam) {
    this.param = param
  }
}

export class CandidateNumCmd {
  protected param: CmdParam
  protected isSuccess: boolean = false

  constructor(param: CmdParam) {
    this.param = param
  }
}

export class InputCertainNumCmd extends CertainNumCmd implements Command {
  constructor(param: CmdParam) {
    super(param)
  }

  execute(sudoku: Sudoku) {
    this.oldNum = sudoku.insertCertainNum({
      ...this.param
    })
    if (this.oldNum === this.param.value) {
      return false
    }
    return true
  }

  recover(sudoku: Sudoku) {
    sudoku.insertCertainNum({
      value: this.oldNum,
      colIdx: this.param.colIdx,
      rowIdx: this.param.rowIdx
    })
  }
}

export class InputCandidateNumCmd extends CandidateNumCmd implements Command {
  constructor(param: CmdParam) {
    super(param)
  }

  execute(sudoku: Sudoku) {
    this.isSuccess = sudoku.insertCandidateNum({
      ...this.param
    })
    if (!this.isSuccess) {
      return false
    }
    return true
  }

  recover(sudoku: Sudoku) {
    if (!this.isSuccess) {
      return
    }
    sudoku.removeCandidateNum({
      ...this.param
    })
  }
}

// Delete num in a sudoku cell
export class DeleteCertainNumCmd extends CertainNumCmd implements Command {
  constructor(param: CmdParam) {
    super(param)
  }

  execute(sudoku: Sudoku) {
    this.oldNum = sudoku.removeCertainNum(this.param)
    // Not Delete
    if (this.oldNum == -1) {
      return false
    }
    return true
  }

  recover(sudoku: Sudoku) {
    sudoku.insertCertainNum({
      colIdx: this.param.colIdx,
      rowIdx: this.param.rowIdx,
      value: this.oldNum
    })
  }
}

// Delete one candidate num in a sudoku cell
export class DeleteCandidateNumCmd extends CandidateNumCmd implements Command {
  constructor(param: CmdParam) {
    super(param)
  }

  execute(sudoku: Sudoku) {
    this.isSuccess = sudoku.removeCandidateNum(this.param)
    return this.isSuccess
  }

  recover(sudoku: Sudoku) {
    if (!this.isSuccess) {
      return
    }
    sudoku.insertCandidateNum({
      colIdx: this.param.colIdx,
      rowIdx: this.param.rowIdx,
      value: this.param.value
    })
  }
}
